/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

Contact:
     SYSTAP, LLC DBA Blazegraph
     2501 Calvert ST NW #106
     Washington, DC 20008
     licenses@blazegraph.com

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package com.bigdata.rdf.sail;

import java.util.Properties;

import org.openrdf.model.BNode;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.impl.BNodeImpl;
import org.openrdf.model.impl.ContextStatementImpl;
import org.openrdf.model.impl.StatementImpl;
import org.openrdf.model.impl.URIImpl;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryResult;

import com.bigdata.rdf.axioms.NoAxioms;
import com.bigdata.rdf.model.BigdataStatement;
import com.bigdata.rdf.store.BD;
import com.bigdata.rdf.vocab.NoVocabulary;

/**
 * @author <a href="mailto:mrpersonick@users.sourceforge.net">Mike Personick</a>
 * @version $Id$
 */
public class TestReadWriteTransactions extends ProxyBigdataSailTestCase {

    /**
     * 
     */
    public TestReadWriteTransactions() {
    }

    /**
     * @param arg0
     */
    public TestReadWriteTransactions(String arg0) {
        super(arg0);
    }

    @Override
    public Properties getProperties() {
        
        Properties props = super.getProperties();
        
        props.setProperty(BigdataSail.Options.ISOLATABLE_INDICES, "true");
        props.setProperty(BigdataSail.Options.TRUTH_MAINTENANCE, "false");
        props.setProperty(BigdataSail.Options.AXIOMS_CLASS, NoAxioms.class.getName());
        props.setProperty(BigdataSail.Options.VOCABULARY_CLASS, NoVocabulary.class.getName());
        props.setProperty(BigdataSail.Options.JUSTIFY, "false");
        props.setProperty(BigdataSail.Options.TEXT_INDEX, "false");
        
        return props;
        
    }
    
    private URI uri(String s) {
        return new URIImpl(BD.NAMESPACE + s);
    }
    
    private BNode bnode(String id) {
        return new BNodeImpl(id);
    }
    
    private Statement stmt(Resource s, URI p, Value o) {
        return new StatementImpl(s, p, o);
    }
    
    private Statement stmt(Resource s, URI p, Value o, Resource c) {
        return new ContextStatementImpl(s, p, o, c);
    }
    
    /**
     * Test the commit semantics in the context of a read-committed view of the
     * database.
     */
    public void test_commit() throws Exception {

        // final LocalTripleStore store = (LocalTripleStore) getStore();
        final BigdataSail sail = getSail();
        try {
        sail.initialize();
        final BigdataSailRepository repo = new BigdataSailRepository(sail);
        final BigdataSailRepositoryConnection isolated = 
            (BigdataSailRepositoryConnection) repo.getReadWriteConnection();
        isolated.setAutoCommit(false);
//        final BigdataSailRepositoryConnection unisolated = 
//            (BigdataSailRepositoryConnection) repo.getUnisolatedConnection();
//        unisolated.setAutoCommit(false);


        // read-committed view of the same database.
        // final AbstractTripleStore view = store.asReadCommittedView();
        RepositoryConnection readView = 
            (BigdataSailRepositoryConnection) repo.getReadOnlyConnection();

        try {

            // final long s = 1, p = 2, o = 3;
            final URI s = uri("a"), p = uri("b"), o = uri("c");

            // add the statement.
//            store.addStatements(new SPO[] { //
//                    new SPO(s, p, o, StatementEnum.Explicit) //
//                    },//
//                    1);
            isolated.add(stmt(s, p, o));
            
//            final boolean stmtInUnisolated = unisolated.hasStatement(s, p, o, true);
//
//            if(log.isInfoEnabled()) log.info("stmtInUnisolated: " + stmtInUnisolated);

            final boolean stmtInIsolated = isolated.hasStatement(s, p, o, true);

            if(log.isInfoEnabled()) log.info("stmtInIsolated: " + stmtInIsolated);

            final boolean stmtInView = readView.hasStatement(s, p, o, true);

            if(log.isInfoEnabled()) log.info("stmtInView: " + stmtInView);

//            // not visible in the repo.
//            assertFalse(stmtInUnisolated);

            // not visible in the view.
            assertFalse(stmtInView);

            // visible in the transaction.
            assertTrue(stmtInIsolated);

            // commit the transaction.
            isolated.commit();

            // verify that the write was published.
            readView = repo.getReadOnlyConnection();
            
            // now visible in the view
            /*
             * Note: this will fail if the Journal#getIndex(name,timestamp) does
             * not return an index view with read-committed (vs read-consistent)
             * semantics. For the index view to have read-committed semantics
             * the view MUST update if there is an intervening commit. This is
             * currently handled by returning a ReadCommittedView for this case
             * rather than a BTree.
             */
            assertTrue(readView.hasStatement(s, p, o, true));
            
        } finally {

            readView.close();
            isolated.close();
//            unisolated.close();

        }
        } finally {
            sail.__tearDownUnitTest();
        }
        
    }
    
    /**
     * Test of abort semantics.
     */
    public void test_abort() throws Exception {

        class AbortException extends RuntimeException {
            private static final long serialVersionUID = 1L;
        }

//        final LocalTripleStore store = (LocalTripleStore) getStore();
        final BigdataSail sail = getSail();
        try {
        sail.initialize();
        final BigdataSailRepository repo = new BigdataSailRepository(sail);
        final RepositoryConnection store = repo.getReadWriteConnection();
        store.setAutoCommit(false);

        // Should be a nop.
        store.rollback();
        
//        final long s = 1, p = 2, o = 3;
        final URI s = uri("a"), p = uri("b"), o = uri("c");

        try {
            
            // add the statement.
//            store.addStatements(new SPO[] { //
//                    new SPO(s, p, o, StatementEnum.Explicit) //
//                    },//
//                    1);
            store.add(stmt(s, p, o));

            // visible in the repo.
            assertTrue(store.hasStatement(s, p, o, true));
            
            // discard the write set.
            store.rollback();

            // no longer visible in the repo.
            assertFalse(store.hasStatement(s, p, o, true));

        } finally {

            store.close();
            
        }
        } finally {
            sail.__tearDownUnitTest();
        }

    }

    public void test_multiple_transaction() throws Exception {

        // final LocalTripleStore store = (LocalTripleStore) getStore();
        final BigdataSail sail = getSail();
        try {
        sail.initialize();
        final BigdataSailRepository repo = new BigdataSailRepository(sail);
        final RepositoryConnection tx1 = repo.getReadWriteConnection();
        tx1.setAutoCommit(false);
        final RepositoryConnection tx2 = repo.getReadWriteConnection();
        tx2.setAutoCommit(false);

        try {

            // final long s = 1, p = 2, o = 3;
            final URI s = uri("a"), p = uri("b"), o = uri("c");

            final BNode sid1 = bnode("1"), sid2 = bnode("2");

            final URI author = uri("author"), mike = uri("mike"), bryan = uri("bryan");
            
            // add the statement.
//            store.addStatements(new SPO[] { //
//                    new SPO(s, p, o, StatementEnum.Explicit) //
//                    },//
//                    1);
            tx1.add(stmt(s, p, o, sid1));
            
            tx1.add(stmt(sid1, author, mike));
            
            tx1.commit();
            
            assertTrue(tx1.hasStatement(s, p, o, true));

            assertFalse(tx2.hasStatement(s, p, o, true));

            tx2.add(stmt(s, p, o, sid2));
            
            tx2.add(stmt(sid2, author, bryan));
            
            tx2.commit();
            
            assertTrue(tx2.hasStatement(s, p, o, true));
            
            RepositoryConnection view = repo.getReadOnlyConnection();

            assertTrue(view.hasStatement(s, p, o, true));
            
            RepositoryResult<Statement> it = view.getStatements(s, p, o, false);
            
            BigdataStatement stmt = (BigdataStatement) it.next();
            
            Resource sid = stmt.getContext();
            
            assertTrue(view.hasStatement(sid, author, mike, true));
            
            assertTrue(view.hasStatement(sid, author, bryan, true));
            
            view.close();

        } finally {

            tx1.close();
            tx2.close();

        }
        } finally {

            sail.__tearDownUnitTest();

        }
        
    }
    
//    /**
//     * @todo Need to test how the system reacts to concurrent reads and writes.
//     * Simulate real work load.  Incremental writes, outnumbered by reads by
//     * some factor, say somewhere between 10 to 100.  Write size will vary, say 
//     * from 1 to 10000 statements.
//     * <p>
//     * How should we generate the data?
//     * N write tasks
//     * each write tasks update information about one "entity"
//     * each "entity" has P properties
//     * each property has A chance of being an attribute and (1-A) chance of being a link to another "entity"
//     * attribute predicate and link predicate can be constants
//     * attribute value is a randomly assigned literal
//     * link value is randomly chosen from pool of already created "entities"  
//     * 
//     * @throws Exception
//     */
//    public void test_stress() throws Exception {
//        
//        final BigdataSail sail = getSail();
//        sail.initialize();
//        final BigdataSailRepository repo = new BigdataSailRepository(sail);
//
//        try {
//
//            
//            
//        } finally {
//
//            sail.__tearDownUnitTest();
//
//        }
//        
//    }
    
}
